//##################################################
//           TEXTURE LOADING CODE                  #
//##################################################

#include "Texturing.h"

//========================================
// set some texture properties           =
//========================================

void TEXTURE::Init(GLint param1, GLint param2, bool s3tc)
{
	tex_param1 = param1;
	tex_param2 = param2;
	tex_comp  = s3tc;
}

//========================================
// nehe iPicture code                    =
//========================================

int TEXTURE::BuildTexture(char *szPathName, GLuint &texid)						// Load Image And Convert To A Texture
{
	HDC			hdcTemp;												// The DC To Hold Our Bitmap
	HBITMAP		hbmpTemp;												// Holds The Bitmap Temporarily
	IPicture	*pPicture;												// IPicture Interface
	OLECHAR		wszPath[MAX_PATH+1];									// Full Path To Picture (WCHAR)
	char		szPath[MAX_PATH+1];										// Full Path To Picture
	long		lWidth;													// Width In Logical Units
	long		lHeight;												// Height In Logical Units
	long		lWidthPixels;											// Width In Pixels
	long		lHeightPixels;											// Height In Pixels
	GLint		glMaxTexDim ;											// Holds Maximum Texture Size

	if (strstr(szPathName, "http://"))									// If PathName Contains http:// Then...
	{
		strcpy(szPath, szPathName);										// Append The PathName To szPath
	}
	else																// Otherwise... We Are Loading From A File
	{
		GetCurrentDirectory(MAX_PATH, szPath);							// Get Our Working Directory
		strcat(szPath, "\\");											// Append "\" After The Working Directory
		strcat(szPath, szPathName);										// Append The PathName
	}

	MultiByteToWideChar(CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);		// Convert From ASCII To Unicode
	HRESULT hr = OleLoadPicturePath(wszPath, 0, 0, 0, IID_IPicture, (void**)&pPicture);

	if(FAILED(hr))														// If Loading Failed
		return FALSE;													// Return False

	hdcTemp = CreateCompatibleDC(GetDC(0));								// Create The Windows Compatible Device Context
	if(!hdcTemp)														// Did Creation Fail?
	{
		pPicture->Release();											// Decrements IPicture Reference Count
		return FALSE;													// Return False (Failure)
	}

	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTexDim);					// Get Maximum Texture Size Supported
	
	pPicture->get_Width(&lWidth);										// Get IPicture Width (Convert To Pixels)
	lWidthPixels	= MulDiv(lWidth, GetDeviceCaps(hdcTemp, LOGPIXELSX), 2540);
	pPicture->get_Height(&lHeight);										// Get IPicture Height (Convert To Pixels)
	lHeightPixels	= MulDiv(lHeight, GetDeviceCaps(hdcTemp, LOGPIXELSY), 2540);

	// Resize Image To Closest Power Of Two
	if (lWidthPixels <= glMaxTexDim) // Is Image Width Less Than Or Equal To Cards Limit
		lWidthPixels = 1 << (int)floor((log((double)lWidthPixels)/log(2.0f)) + 0.5f); 
	else  // Otherwise  Set Width To "Max Power Of Two" That The Card Can Handle
		lWidthPixels = glMaxTexDim;
 
	if (lHeightPixels <= glMaxTexDim) // Is Image Height Greater Than Cards Limit
		lHeightPixels = 1 << (int)floor((log((double)lHeightPixels)/log(2.0f)) + 0.5f);
	else  // Otherwise  Set Height To "Max Power Of Two" That The Card Can Handle
		lHeightPixels = glMaxTexDim;
	
	//	Create A Temporary Bitmap
	BITMAPINFO	bi = {0};												// The Type Of Bitmap We Request
	DWORD		*pBits = 0;												// Pointer To The Bitmap Bits

	bi.bmiHeader.biSize			= sizeof(BITMAPINFOHEADER);				// Set Structure Size
	bi.bmiHeader.biBitCount		= 32;									// 32 Bit
	bi.bmiHeader.biWidth		= lWidthPixels;							// Power Of Two Width
	bi.bmiHeader.biHeight		= lHeightPixels;						// Make Image Top Up (Positive Y-Axis)
	bi.bmiHeader.biCompression	= BI_RGB;								// RGB Encoding
	bi.bmiHeader.biPlanes		= 1;									// 1 Bitplane

	//	Creating A Bitmap This Way Allows Us To Specify Color Depth And Gives Us Imediate Access To The Bits
	hbmpTemp = CreateDIBSection(hdcTemp, &bi, DIB_RGB_COLORS, (void**)&pBits, 0, 0);
	
	if(!hbmpTemp)														// Did Creation Fail?
	{
		DeleteDC(hdcTemp);												// Delete The Device Context
		pPicture->Release();											// Decrements IPicture Reference Count
		return FALSE;													// Return False (Failure)
	}

	SelectObject(hdcTemp, hbmpTemp);									// Select Handle To Our Temp DC And Our Temp Bitmap Object

	// Render The IPicture On To The Bitmap
	pPicture->Render(hdcTemp, 0, 0, lWidthPixels, lHeightPixels, 0, lHeight, lWidth, -lHeight, 0);

	// Convert From BGR To RGB Format And Add An Alpha Value Of 255
	for(long i = 0; i < lWidthPixels * lHeightPixels; i++)				// Loop Through All Of The Pixels
	{
		BYTE* pPixel	= (BYTE*)(&pBits[i]);							// Grab The Current Pixel
		BYTE  temp		= pPixel[0];									// Store 1st Color In Temp Variable (Blue)
		pPixel[0]		= pPixel[2];									// Move Red Value To Correct Position (1st)
		pPixel[2]		= temp;											// Move Temp Value To Correct Blue Position (3rd)

		// This Will Make Any Black Pixels, Completely Transparent		(You Can Hardcode The Value If You Wish)
		if ((pPixel[0]==0) && (pPixel[1]==0) && (pPixel[2]==0))			// Is Pixel Completely Black
			pPixel[3]	=   0;											// Set The Alpha Value To 0
		else															// Otherwise
			pPixel[3]	= 255;											// Set The Alpha Value To 255
	}

	glGenTextures(1, &texid);											// Create The Texture

	// Typical Texture Generation Using Data From The Bitmap
	glBindTexture(GL_TEXTURE_2D, texid);								// Bind To The Texture ID
   
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,tex_param1);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,tex_param2);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);  //needed for the lightmap 
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	if(tex_comp)
	 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, lWidthPixels, lHeightPixels, GL_RGBA, GL_UNSIGNED_BYTE, pBits);
    else
	 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, lWidthPixels, lHeightPixels, GL_RGBA, GL_UNSIGNED_BYTE, pBits);	

	DeleteObject(hbmpTemp);												// Delete The Object
	DeleteDC(hdcTemp);													// Delete The Device Context

	pPicture->Release();												// Decrements IPicture Reference Count

	return TRUE;														// Return True (All Good)
}

//========================================
// get the texture from the resource     =
//========================================

int TEXTURE::CreateResTexture(int image, GLuint &texid)
{
	HBITMAP h;
	

	if (GetModuleHandle(0))
		h = (HBITMAP)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(image),IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
	else
		h = (HBITMAP)LoadImage(0, MAKEINTRESOURCE(image), IMAGE_BITMAP, 0, 0,LR_LOADFROMFILE | LR_CREATEDIBSECTION);

	if (h) 
	{
		DIBSECTION ds;
		GetObject(h, sizeof(ds), &ds);
		int sz=(ds.dsBmih.biWidth<ds.dsBmih.biHeight)?ds.dsBmih.biWidth:ds.dsBmih.biHeight;
		double d=log(sz)/log(2);
		int txbits = (int)floor(d+0.01);
		int txsize = 1 << txbits;
		DeleteObject(h);
		if (GetModuleHandle(0))
			h = (HBITMAP)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(image),IMAGE_BITMAP, txsize, txsize, LR_CREATEDIBSECTION);
		else
			h = (HBITMAP)LoadImage(0, MAKEINTRESOURCE(image), IMAGE_BITMAP,txsize, txsize, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
		GetObject(h, sizeof(ds), &ds);

		BITMAPINFOHEADER a = {
			sizeof(BITMAPINFOHEADER),
			txsize,
			-txsize,
			1,
			24,
			BI_RGB,
			3*txsize*txsize,  /* 3 BGR, 4 BGRA */
			0,
			0,
			0,
			0
			};
	
		HDC dc;

		dc=GetDC(NULL);

		unsigned char *texturebis = new unsigned char[a.biSizeImage];
		GetDIBits(dc, h, 0, txsize, texturebis, (BITMAPINFO *)&a,DIB_RGB_COLORS);
		
		glGenTextures(1, &texid); 
		glBindTexture(GL_TEXTURE_2D, texid);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,tex_param1);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,tex_param2);
		gluBuild2DMipmaps(GL_TEXTURE_2D, 3, txsize, txsize, GL_RGB, GL_UNSIGNED_BYTE, texturebis);
		DeleteObject(h);
		ReleaseDC(NULL,dc);
		return true;
	}
	else
	   return false;
}

//========================================
// render 2 texture stuff :-)            =
//========================================

int TEXTURE::CreateRenderTexture(int size, int channels, int type, GLuint &textureID)										
{
	unsigned int *pTexture = NULL;											

	pTexture = new unsigned int [size * size * channels];
	memset(pTexture, 0, size * size * channels * sizeof(unsigned int));	

	glGenTextures(1, &textureID);								
	glBindTexture(GL_TEXTURE_2D, textureID);					
	
	glTexImage2D(GL_TEXTURE_2D, 0, channels, size, size, 0, type, GL_UNSIGNED_INT, pTexture);						
	
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,tex_param1);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,tex_param1);

	delete [] pTexture;	
	return true;
}

void TEXTURE::SetViewport(int width, int height)
{
	glViewport(0, 0, width, height);
}

void TEXTURE::RenderTo(int viewport_x, int viewport_y, int screen_x, int screen_y, GLuint &texid)
{
	glBindTexture(GL_TEXTURE_2D,texid);				
	glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, viewport_x, viewport_y, 0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );			
	glViewport(0, 0, screen_x, screen_y);
}

//========================================
// create a cubic environment map :-D    =
//========================================

/* we don't need this here =)

void TEXTURE::LoadCubeMap(char *name1, char *name2, char *name3, char *name4, char *name5, char *name6, GLuint &texid)
{
	GLuint cube_map_directions[6] = 
	{
     GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
     GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
     GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
     GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
     GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
     GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
	};

	AUX_RGBImageRec *image[6];

	glEnable(GL_TEXTURE_CUBE_MAP_ARB);

	image[0] = auxDIBImageLoad(name1);
	image[1] = auxDIBImageLoad(name2);
	image[2] = auxDIBImageLoad(name3);
	image[3] = auxDIBImageLoad(name4);
	image[4] = auxDIBImageLoad(name5);
	image[5] = auxDIBImageLoad(name6);

	glGenTextures(1, &texid);

	//Bind the texture.
	glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, texid);

	for(int i=0; i<6; i++)
	{
		glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, tex_param2);
		glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, tex_param1);

		gluBuild2DMipmaps(cube_map_directions[i], GL_RGB8, image[i]->sizeX, image[i]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, image[i]->data);
	    delete [] image[i]->data;
	}

	glDisable(GL_TEXTURE_CUBE_MAP_ARB);
}

void TEXTURE::BindCubemap(GLuint &texture)
{
	   glEnable(GL_TEXTURE_CUBE_MAP_ARB);
	   glDisable(GL_TEXTURE_2D);
	   glEnable(GL_TEXTURE_GEN_R) ;
	   glEnable(GL_TEXTURE_GEN_S) ;
       glEnable(GL_TEXTURE_GEN_T) ;
	   glTexGeni(GL_R,GL_TEXTURE_GEN_MODE,GL_REFLECTION_MAP_EXT);
       glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_REFLECTION_MAP_EXT);
       glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_REFLECTION_MAP_EXT);

	   glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, texture);
}
*/

//========================================
// build the texture you want            =
//========================================

int TEXTURE::Generate(int mode, char *szPathName, int res_id, GLuint &texid)
{
	int status;

	if(mode==TEX_RESOURCE)
	{
		status=CreateResTexture(res_id,texid);
	}
	if(mode==TEX_RENDER)
	{
		status=CreateRenderTexture(512, 3, GL_RGB8, texid);
	}
	if(mode==TEX_2D)
	{
		status=BuildTexture(szPathName, texid);
	}

	return status;
}
